diff options
| author | Adam Soltys <[email protected]> | 2024-09-10 20:39:47 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-09-17 09:18:36 -0700 |
| commit | 1f63848ca9fb01336f04b41434de1cc06a5b3d93 (patch) | |
| tree | 2affa3041863be365f33d99e1f32bda60cf93a32 /src/routes/(app)/[username] | |
| parent | 6e48c584605bec5623c12c470d04d07303983801 (diff) | |
| download | coinos-ui-1f63848ca9fb01336f04b41434de1cc06a5b3d93.tar.gz coinos-ui-1f63848ca9fb01336f04b41434de1cc06a5b3d93.zip | |
move routes
Diffstat (limited to 'src/routes/(app)/[username]')
50 files changed, 20 insertions, 3276 deletions
diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte index 2caf9b30..111fdeb6 100644 --- a/src/routes/(app)/[username]/(profile)/+layout.svelte +++ b/src/routes/(app)/[username]/(profile)/+layout.svelte @@ -219,7 +219,7 @@ {#if user && user.username !== subject.username && subject.pubkey} <a - href={`/${user.username}/messages/${subject.username}`} + href={`/messages/${subject.username}`} class="contents" > <button diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index 25244c6d..9e5babd2 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -26,16 +26,16 @@ if (user) user.savings = 0; </script> -{#if !user.balance} - <div class="mb-8"> - <p class="text-secondary text-lg"> - {$t("user.welcome")} - </p> - </div> -{/if} - <div class="space-y-2"> {#if user?.id === subject.id} + {#if !user.balance} + <div class="mb-8"> + <p class="text-secondary text-lg"> + {$t("user.welcome")} + </p> + </div> + {/if} + {#each accounts as account} <Account {user} {rate} {account} /> {/each} @@ -43,7 +43,7 @@ <button class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-black text-white" > - <a href={`/${user.username}/account/setup`}> + <a href={`/account/setup`}> <div class="mx-auto flex gap-2"> <Icon icon="plus" style="w-8 mx-auto invert" /> <div class="my-auto text-xl whitespace-nowrap">Add account</div> diff --git a/src/routes/(app)/[username]/+layout.server.ts b/src/routes/(app)/[username]/+layout.server.ts index 11e6d45c..c340544d 100644 --- a/src/routes/(app)/[username]/+layout.server.ts +++ b/src/routes/(app)/[username]/+layout.server.ts @@ -1,17 +1,25 @@ import { get } from "$lib/utils"; import { error } from "@sveltejs/kit"; -export let load = async ({ cookies, depends, params: { username } }) => { +export let load = async ({ + cookies, + depends, + params: { username }, + parent, +}) => { depends("app:user"); try { let subject = await get(`/users/${username}`); + let { user } = await parent(); + if (!user) user = subject; + console.log("SUBJECT", subject) let expires = new Date(); expires.setSeconds(expires.getSeconds() + 380 * 24 * 60 * 60); cookies.set("username", username, { path: "/", expires }); - return { subject }; + return { subject, user }; } catch (e) { error(500, "Unable to retrieve user account data"); } diff --git a/src/routes/(app)/[username]/+layout.svelte b/src/routes/(app)/[username]/+layout.svelte index cd517fa9..98569f59 100644 --- a/src/routes/(app)/[username]/+layout.svelte +++ b/src/routes/(app)/[username]/+layout.svelte @@ -1,5 +1,4 @@ <script> - import AppHeader from "$comp/AppHeader.svelte"; import { onNavigate } from "$app/navigation"; export let data; @@ -15,7 +14,6 @@ }); </script> -<AppHeader {data} /> <div class="content"> <slot /> </div> diff --git a/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.server.ts b/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.server.ts deleted file mode 100644 index c2cd5683..00000000 --- a/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.server.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { get, auth } from "$lib/utils"; - -let limit = 25; - -export async function load({ cookies, depends, params }) { - depends("app:payments"); - - let parts = params.page.split("/"); - - let start, end, page; - if (parts.length === 1) [page] = parts; - if (parts.length === 2) [start, page] = parts; - if (parts.length === 3) [start, end, page] = parts; - if (!parseInt(page)) page = 1; - - let offset = (page - 1) * limit; - if (!start) start = Date.now() / 1000 - 24 * 60 * 60; - - let url = `/payments?account=${params.account}&start=${ - start * 1000 - }&limit=${limit}&offset=${offset}`; - if (end) url += `&end=${end * 1000}`; - - let { count, payments, incoming, outgoing } = await get(url, auth(cookies)); - let pages = new Array(Math.ceil(count / limit)); - - return { payments, page, pages, start, end, incoming, outgoing }; -} diff --git a/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.svelte b/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.svelte deleted file mode 100644 index 1efdc2ec..00000000 --- a/src/routes/(app)/[username]/[...account]/payments/[...page]/+page.svelte +++ /dev/null @@ -1,315 +0,0 @@ -<script> - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import { onMount } from "svelte"; - import { format } from "date-fns"; - import { newPayment } from "$lib/store"; - import { t } from "$lib/translations"; - import { get, f, s, sat, sats, types } from "$lib/utils"; - import { page } from "$app/stores"; - import { differenceInDays, getUnixTime, sub } from "date-fns"; - import { goto, invalidate } from "$app/navigation"; - - export let data; - - let { start, end, user, rates, payments } = data; - - let change = ({ target: { value } }) => goto(value); - let link = (p) => { - if (p.pot) return `/pot/${p.pot}`; - if (p.with) return "/" + p.with.username; - return `/p/${p.id}`; - }; - - $: presets = [ - { - title: $t("payments.day"), - start: sub(new Date(), { days: 1 }), - end: null, - }, - { - title: $t("payments.week"), - start: sub(new Date(), { days: 7 }), - end: null, - }, - { - title: $t("payments.month"), - start: sub(new Date(), { months: 1 }), - end: null, - }, - { - title: $t("payments.all"), - start: sub(new Date(), { years: 5 }), - end: null, - }, - ]; - - $: selection = start - ? presets.findIndex( - (p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1 - ) - : 0; - - let p, - incoming, - outgoing, - pages = []; - $: data && - ({ page: p, pages, start, end, incoming, outgoing, payments } = data); - - $: $page && ($newPayment = false); - $: $newPayment && invalidate(`/users/${user.username}`); - - $: path = $page.params.page - ? $page.url.pathname.substring(0, $page.url.pathname.lastIndexOf("/")) - : $page.url.pathname; - - let csv = async () => { - let url = `/${user.username}/payments`; - if (start) url += `/${start}`; - if (end) url += `/${end}`; - - let { payments } = await get(url); - - payments = payments.map((p) => ({ - ...p, - created: new Date(p.created), - total: p.amount + (p.tip || 0), - platform_fee: p.ourfee, - amount_fiat: f((p.amount * p.rate) / sats, p.currency), - fee_fiat: p.fee ? f((p.fee * p.rate) / sats, p.currency) : null, - platform_fee_fiat: p.ourfee ? f((p.ourfee * p.rate) / sats, p.currency) : null, - tip_fiat: p.tip ? f((p.tip * p.rate) / sats, p.currency) : null, - total_fiat: f(((p.amount + (p.tip || 0)) * p.rate) / sats, p.currency), - })); - - let keys = [ - "type", - "id", - "created", - "rate", - "currency", - "amount", - "tip", - "total", - "fee", - "platform_fee", - "amount_fiat", - "tip_fiat", - "total_fiat", - "fee_fiat", - "platform_fee_fiat", - ]; - - let csv = - keys.map((k) => `"${k}"`).join(",") + - "\n" + - payments - .map((r) => keys.map((k) => `"${r[k] || ""}"`).join(",")) - .join("\n"); - - let filename = "payments.csv"; - let blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); - if (navigator.msSaveBlob) { - navigator.msSaveBlob(blob, filename); - } else { - let link = document.createElement("a"); - if (link.download !== undefined) { - let url = URL.createObjectURL(blob); - link.setAttribute("href", url); - link.setAttribute("download", filename); - link.style.visibility = "hidden"; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - } - } - }; -</script> - -<div class="mt-24 mb-20 payments"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> - {$t("payments.header")} - </h1> - - <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <div class="flex text-md text-secondary relative"> - <div class="mx-auto flex justify-center w-full gap-1"> - {#each presets as { start, end, title }, i} - <a - class:active={selection === i} - href={`/${user.username}/payments/${getUnixTime(start) + "/"}1`} - > - <button - class="text-sm md:text-lg rounded-full border py-2 px-4 hover:opacity-80 min-w-[72px]" - > - <div class="my-auto">{title}</div> - </button> - </a> - {/each} - </div> - </div> - - <div class="flex flex-wrap justify-center mb-8"> - {#if pages.length > 1} - {#each pages as _, i} - <a - class="mr-1 last:mr-0" - href={`${path}/${i + 1}`} - class:active={parseInt(p) === i + 1} - > - <div - class="border py-2 rounded-full border-2 w-12 h-12 hover:opacity-80 text-center" - > - {i + 1} - </div> - </a> - {/each} - {/if} - </div> - - <div class="text-base"> - {#each payments as p} - <a href={`/payment/${p.id}`}> - <div class="grid grid-cols-12 border-b h-24 hover:bg-gray-100 px-4"> - <div class="whitespace-nowrap my-auto col-span-3"> - <div class="font-bold" class:text-red-800={p.amount < 0}> - {f(Math.abs(p.amount) * (p.rate / sats), p.currency)} - {#if p.tip} - <span class="text-sm text-secondary"> - +{Math.round((p.tip / Math.abs(p.amount)) * 100)}% - </span> - {/if} - </div> - - <div class="text-secondary"> - {sat(Math.abs(p.amount) + (p.tip || 0))} - </div> - </div> - - <div - class="flex my-auto col-span-5 truncate text-ellipsis overflow-hidden mx-auto" - > - {#if p.type === types.pot} - <a href={`/pot/${p.memo}`}> - <div class="text-secondary flex"> - <div class="my-auto mr-1"> - <img src="/images/moneypot.png" class="w-12" alt="Pot" /> - </div> - - <div class="my-auto">Pot</div> - </div> - </a> - {:else if p.with} - <div class="flex"> - <div class="my-auto"> - <Avatar user={p.with} size={12} disabled={true} /> - </div> - <div class="my-auto ml-1 text-secondary"> - {p.with.username} - </div> - </div> - {:else} - <div class="text-secondary flex"> - {#if [types.ecash, types.lightning].includes(p.type)} - <div class="text-3xl">⚡️</div> - {:else if p.type === types.bitcoin} - <div class="my-auto mr-1"> - <img - src="/images/bitcoin.svg" - class="w-12 border-4 border-transparent" - alt="Bitcoin" - /> - </div> - {:else if p.type === types.liquid} - <div class="my-auto mr-1"> - <img - src="/images/liquid.svg" - class="w-12 border-4 border-transparent" - alt="Liquid" - /> - </div> - {/if} - - <div class="my-auto"> - {p.amount > 0 - ? p.confirmed - ? "Received" - : "Pending" - : "Sent"} - </div> - </div> - {/if} - </div> - - <div class="text-secondary text-right text-sm my-auto col-span-3"> - <div> - {format(new Date(p.created), "h:mm aaa")} - </div> - <div> - {format(new Date(p.created), "MMM d, yy")} - </div> - </div> - </div> - </a> - {:else} - <p class="text-secondary text-lg text-center">{$t("payments.empty")}</p> - {/each} - </div> - - <div class="grid grid-cols-3 w-full text-center text-lg"> - {#each Object.keys(incoming) as c} - <span class="text-base text-secondary text-left"></span> - <!-- <span class="text-base text-secondary" --> - <!-- >{$t("payments.subtotal")}</span --> - <!-- > --> - <span class="text-base text-secondary"> - {#if tipsIn || tipsOut}{$t("payments.tips")}{/if} - </span> - <span class="text-base text-secondary" - >{$t("payments.total")}</span - > - - {@const totalIn = incoming[c]?.fiat || 0} - {@const tipsIn = incoming[c]?.fiatTips || 0} - {@const subtotalIn = totalIn - tipsIn} - - {@const totalOut = -outgoing[c]?.fiat || 0} - {@const tipsOut = outgoing[c]?.fiatTips || 0} - {@const subtotalOut = totalOut - tipsOut} - - <span class="text-left text-base text-secondary" - >{$t("payments.income")}</span - > - <!-- <span><b>{subtotalIn ? f(subtotalIn, c) : "-"}</b></span> --> - <span><b>{tipsIn ? f(tipsIn, c) : "-"}</b></span> - <span><b>{totalIn ? f(totalIn, c) : "-"}</b></span> - - <span class="text-left text-base text-secondary" - >{$t("payments.expenditure")}</span - > - <!-- <span><b>{subtotalOut ? f(subtotalOut, c) : "-"}</b></span> --> - <span><b>{tipsOut ? f(tipsOut, c) : "-"}</b></span> - <span><b>{totalOut ? f(totalOut, c) : "-"}</b></span> - {/each} - </div> - - <button - class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto" - on:click={csv} - > - <Icon icon="save" style="opacity-50 mr-2 my-auto" /> - <div class="my-auto">{$t("payments.export")}</div> - </button> - </div> -</div> - -<style> - .active * { - @apply bg-black text-white border-black; - } - - .payments { - view-transition-name: payments; - } -</style> diff --git a/src/routes/(app)/[username]/[...account]/payments/[...page]/+server.ts b/src/routes/(app)/[username]/[...account]/payments/[...page]/+server.ts deleted file mode 100644 index a81faaf3..00000000 --- a/src/routes/(app)/[username]/[...account]/payments/[...page]/+server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { get, auth } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function GET({ cookies, params }) { - let parts = params.page.split("/"); - - let start, end; - if (parts.length === 1) [start] = parts; - if (parts.length === 2) [start, end] = parts; - - if (!start) start = Date.now() / 1000 - 24 * 60 * 60; - - let url = `/payments?start=${start * 1000}`; - if (end) url += `&end=${end * 1000}`; - - let { payments } = await get(url, auth(cookies)); - return json({ payments }); -} diff --git a/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.server.ts b/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.server.ts deleted file mode 100644 index d9614a1c..00000000 --- a/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.server.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { error, redirect } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; -import type { Invoice } from "$lib/types"; - -export let load = async ({ cookies, params, parent }) => { - let { account } = params; - let { subject, user, rates } = await parent(); - - let invoice: Invoice = { - account, - type: account ? "bitcoin" : "lightning", - rate: rates[user?.currency || subject?.currency], - }; - - if (!user) user = subject; - - try { - invoice = await post("/invoice", { invoice, account, user }, auth(cookies)); - } catch (e) { - console.log(e); - error(500, "Failed to generate invoice"); - } - - let { id } = invoice; - - if (invoice.memoPrompt && !invoice.memo) { - redirect(307, `/${user.username}/invoice/${id}/memo`); - } else redirect(307, `/${user.username}/invoice/${id}`); -}; diff --git a/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.svelte b/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.svelte deleted file mode 100644 index f9267105..00000000 --- a/src/routes/(app)/[username]/[...account]/receive/[...param]/+page.svelte +++ /dev/null @@ -1,116 +0,0 @@ -<script> - import { page } from "$app/stores"; - import { fly } from "svelte/transition"; - import { enhance } from "$app/forms"; - import { tick } from "svelte"; - import { browser } from "$app/environment"; - import { t } from "$lib/translations"; - import AppHeader from "$comp/AppHeader.svelte"; - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import Spinner from "$comp/Spinner.svelte"; - import { back, fail, focus } from "$lib/utils"; - - export let data; - export let form; - data.subject = data.user; - - let { contacts } = data; - let w; - - let el, text; - - let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click()); - - let paste = async () => { - text = await navigator.clipboard.readText(); - }; -</script> - -<svelte:window bind:innerWidth={w} /> - -<AppHeader {data} /> -<div class="container px-4 max-w-lg mx-auto space-y-5 mt-20"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold"> - {$t("payments.send")} - </h1> - - <form method="POST" use:enhance class="space-y-2 text-xl"> - {#if form?.error} - <div class="mb-5"> - <div class="text-red-600"> - {$t("error.unrecognizedInput")} - </div> - </div> - {/if} - - <div class="mb-2"> - <textarea - use:focus - name="text" - placeholder={$t("user.send.placeholder")} - on:keypress={keypress} - class="w-full p-4 border rounded-xl h-48 text-xl" - bind:value={text} - autocapitalize="none" - /> - </div> - - <div class="flex gap-2"> - <a href="/scan" class="w-full"> - <button - type="button" - class="flex border rounded-2xl px-6 py-5 font-bold hover:opacity-80 w-full bg-primary justify-center" - > - <Icon icon="camera" style="mr-2 w-6 my-auto" /> - <div class="my-auto">{$t("user.send.scan")}</div> - </button> - </a> - - <button - type="button" - class="flex border rounded-2xl px-6 py-5 font-bold hover:opacity-80 w-full bg-primary justify-center" - on:click={paste} - > - <Icon icon="paste" style="mr-2 w-6 my-auto" /> - <div class="my-auto">{$t("user.send.paste")}</div> - </button> - </div> - - <button - bind:this={el} - type="submit" - class="bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold" - > - {$t("user.send.next")} - </button> - </form> - - {#if contacts.length} - <div class="space-y-5"> - <h1 class="px-3 md:px-0 text-2xl font-semibold mt-10"> - {$t("user.send.contacts")} - </h1> - <div> - {#each contacts as c} - <a href={`/pay/${c.username}`}> - <div - class="border-b p-2 last:border-b-0 hover:bg-gray-100 rounded-2xl" - > - <div class="flex"> - <div> - <div class="flex"> - <Avatar user={c} size={20} disabled={true} /> - <div class="my-auto text-left"> - <p class="ml-1 text-lg break-words">{c.username}</p> - </div> - </div> - </div> - </div> - </div> - </a> - {/each} - </div> - </div> - {/if} -</div> diff --git a/src/routes/(app)/[username]/account/setup/+page.svelte b/src/routes/(app)/[username]/account/setup/+page.svelte deleted file mode 100644 index 9636ff2a..00000000 --- a/src/routes/(app)/[username]/account/setup/+page.svelte +++ /dev/null @@ -1,64 +0,0 @@ -<script> - import { copy } from "$lib/utils"; - import { t } from "$lib/translations"; - import Icon from "$comp/Icon.svelte"; - import { mnemonic } from "$lib/store"; - import { generateMnemonic } from "@scure/bip39"; - import { wordlist } from "@scure/bip39/wordlists/english"; - - export let data; - let { user } = data; - - let reveal; - - let toggleSeed = async () => { - try { - $mnemonic = await generateMnemonic(wordlist); - reveal = !reveal; - } catch (e) { - console.log(e); - } - }; -</script> - -<div class="space-y-5"> - <h1 class="text-center text-3xl font-semibold">Account setup</h1> - <h2 class="text-center text-2xl text-secondary">Step 1</h2> - - <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <p class="text-secondary mb-1"> - The keys to your savings account are derived from a seed phrase. When - you're ready, write it down and keep it safe. - </p> - - <p class="text-secondary mb-1"> - Whoever knows these words in the correct order can withdraw funds from the account. We can't - recover them if you lose them. - </p> - - {#if reveal} - <div class="text-2xl border p-4 text-center rounded-2xl"> - {$mnemonic} - </div> - - <div> - <a href={`/${user.username}/account/setup/step2`} class="contents"> - <button - type="button" - class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" - on:click={toggleSeed} - > - I've written down my seed phrase - </button> - </a> - </div> - {:else} - <div class="flex justify-center"> - <button type="button" class="primary" on:click={toggleSeed}> - <Icon icon="warning" style="mr-2 w-6 my-auto" /> - {$t("user.settings.revealSeed")} - </button> - </div> - {/if} - </div> -</div> diff --git a/src/routes/(app)/[username]/account/setup/step2/+page.svelte b/src/routes/(app)/[username]/account/setup/step2/+page.svelte deleted file mode 100644 index 8169ce06..00000000 --- a/src/routes/(app)/[username]/account/setup/step2/+page.svelte +++ /dev/null @@ -1,52 +0,0 @@ -<script lang="ts"> - import { focus } from "$lib/utils"; - import { t } from "$lib/translations"; - import { mnemonic } from "$lib/store"; - import Icon from "$comp/Icon.svelte"; - import { encrypt, decrypt } from "nostr-tools/nip49"; - import { mnemonicToEntropy, entropyToMnemonic } from "@scure/bip39"; - import { wordlist } from "@scure/bip39/wordlists/english"; - import { fail, post } from "$lib/utils"; - import { goto } from "$app/navigation"; - - export let data; - let { user } = data; - - let password; - - let submit = async () => { - let seed = await encrypt(mnemonicToEntropy($mnemonic, wordlist), password); - try { - await post("/api/accounts", { seed }); - goto(`/${user.username}`); - } catch (e) { - console.log(e); - fail("Something went wrong"); - } - }; -</script> - -<div class="space-y-5"> - <div> - <h1 class="text-center text-3xl font-semibold">Account setup</h1> - <h2 class="text-center text-2xl text-secondary">Step 2</h2> - </div> - - <form - on:submit|preventDefault={submit} - class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5" - > - <p class="text-secondary mb-1"> - Add an additional password to your seed phrase. It should be different - than your login password. We can't reset it if you lose it. - </p> - - <input use:focus type="text" placeholder="Password" bind:value={password} /> - - <button - class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" - > - Submit - </button> - </form> -</div> diff --git a/src/routes/(app)/[username]/address/+page.server.ts b/src/routes/(app)/[username]/address/+page.server.ts deleted file mode 100644 index 2f7841ab..00000000 --- a/src/routes/(app)/[username]/address/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { redirect } from "@sveltejs/kit"; - -export async function load({ cookies, parent }) { - let user = (await parent()).subject; - let invoice = { type: "bitcoin" }; - let { id } = await post(`/invoice`, { invoice, user }, auth(cookies)); - redirect(307, `/${user.username}/invoice/${id}`); -} diff --git a/src/routes/(app)/[username]/address/+page.svelte b/src/routes/(app)/[username]/address/+page.svelte deleted file mode 100644 index e69de29b..00000000 --- a/src/routes/(app)/[username]/address/+page.svelte +++ /dev/null diff --git a/src/routes/(app)/[username]/card/[serial]/+page.server.ts b/src/routes/(app)/[username]/card/[serial]/+page.server.ts deleted file mode 100644 index 945eee30..00000000 --- a/src/routes/(app)/[username]/card/[serial]/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { auth, get } from "$lib/utils"; -import { redirect } from "@sveltejs/kit"; - -export let load = async ({ cookies, params }) => { - let { serial } = params; - let fund; - try { - fund = await get(`/fund/${serial}`, auth(cookies)); - } catch (e) { - console.log(e); - } - - if (fund) redirect(307, `/fund/${serial}`); - - return params; -}; diff --git a/src/routes/(app)/[username]/card/[serial]/+page.svelte b/src/routes/(app)/[username]/card/[serial]/+page.svelte deleted file mode 100644 index 1d11ebee..00000000 --- a/src/routes/(app)/[username]/card/[serial]/+page.svelte +++ /dev/null @@ -1,36 +0,0 @@ -<script> - import Card from "$comp/Card.svelte"; - import Icon from "$comp/Icon.svelte"; - export let data; - let { serial } = data; - let balance = 0; -</script> - -<div class="space-y-5"> - <div class="relative flex justify-center gap-2"> - <h1 class="text-2xl md:text-3xl font-semibold my-auto">Card {serial}</h1> - </div> - <div class="flex justify-center"> - <a href={`/send/pot/${serial}`}> - <button - class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60 bg-black text-white justify-center gap-2" - > - <svg - xmlns="http://www.w3.org/2000/svg" - class="invert text-black peer-placeholder-shown:text-gray-300 h-6 w-6 my-auto" - fill="none" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" - /> - </svg> - <div class="my-auto">Add Funds</div> - </button> - </a> - </div> -</div> diff --git a/src/routes/(app)/[username]/customers/+page.svelte b/src/routes/(app)/[username]/customers/+page.svelte deleted file mode 100644 index e69de29b..00000000 --- a/src/routes/(app)/[username]/customers/+page.svelte +++ /dev/null diff --git a/src/routes/(app)/[username]/funds/+page.server.ts b/src/routes/(app)/[username]/funds/+page.server.ts deleted file mode 100644 index e69de29b..00000000 --- a/src/routes/(app)/[username]/funds/+page.server.ts +++ /dev/null diff --git a/src/routes/(app)/[username]/funds/+page.svelte b/src/routes/(app)/[username]/funds/+page.svelte deleted file mode 100644 index 25686e86..00000000 --- a/src/routes/(app)/[username]/funds/+page.svelte +++ /dev/null @@ -1,52 +0,0 @@ -<script> - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import { t } from "$lib/translations"; - - export let data; - - let { funds } = data; -</script> - -<div class="mt-24 mb-20"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> - Funds - </h1> - - <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <div class="text-base"> - <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4"> - <div class="whitespace-nowrap my-auto"> - <div class="text-secondary" /> - </div> - - <div class="flex my-auto"> - <div class="text-secondary flex"> - <div class="my-auto mr-1"> - <img src="/images/moneypot.png" class="w-12" alt="Pot" /> - </div> - - <div class="my-auto">Pot</div> - </div> - </div> - - <div class="text-secondary text-right text-sm my-auto"> - <div /> - </div> - </div> - </div> - - <button - class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto" - > - <Icon icon="plus" style="opacity-50 mr-2 my-auto" /> - <div class="my-auto">{$t("payments.create")}</div> - </button> - </div> -</div> - -<style> - .active * { - @apply bg-black text-white border-black; - } -</style> diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte deleted file mode 100644 index 767b7545..00000000 --- a/src/routes/(app)/[username]/generate/+page.svelte +++ /dev/null @@ -1,60 +0,0 @@ -<script> - import { fail, post, wait } from "$lib/utils"; - import { onMount } from "svelte"; - import { goto, invalidate } from "$app/navigation"; - import Pin from "$comp/Pin.svelte"; - import Spinner from "$comp/Spinner.svelte"; - import { loginRedirect, pin, password } from "$lib/store"; - import { generate } from "$lib/nostr"; - import { browser } from "$app/environment"; - - export let data; - let { user } = data; - - let loaded; - let gen = async () => { - if (!browser) return; - - if (user.pubkey) { - goto($loginRedirect || `/${user.username}`, { invalidateAll: true }); - return; - } - - try { - await generate(user); - } catch (e) { - console.log("error generating", e); - } - - try { - user.pin = $pin; - await post(`/${user.username}/generate`, user); - goto($loginRedirect || `/${user.username}`, { invalidateAll: true }); - } catch (e) { - $pin = ""; - if (e.message?.startsWith("Pin")) { - fail("Wrong pin, try again"); - } else { - fail("Failed to generate keys"); - goto("/"); - } - } - }; - - onMount(() => browser && setTimeout(() => (loaded = true) && gen(), 50)); - - $: $pin?.length === 6 && gen(user); -</script> - -{#if loaded && user?.haspin && $pin?.length !== 6} - <Pin /> -{/if} - -<div class="container px-4 text-center mx-auto"> - <h1 class="text-3xl md:text-4xl font-bold mb-6"> - Generating new account keys - </h1> - <div class="flex w-full py-5 max-w-[200px] mx-auto bg-black rounded-2xl"> - <Spinner /> - </div> -</div> diff --git a/src/routes/(app)/[username]/generate/+server.ts b/src/routes/(app)/[username]/generate/+server.ts deleted file mode 100644 index 5a442982..00000000 --- a/src/routes/(app)/[username]/generate/+server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { error, json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - try { - let body = await request.json(); - return json(await post(`/user`, body, auth(cookies))); - } catch (e: any) { - console.log(e); - error(500, e.message); - } -} diff --git a/src/routes/(app)/[username]/invoice/+server.ts b/src/routes/(app)/[username]/invoice/+server.ts deleted file mode 100644 index 5e3d0300..00000000 --- a/src/routes/(app)/[username]/invoice/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export let POST = async ({ cookies: c, request: r }) => { - let j = await r.json(); - return json(await post("/invoice", j, auth(c))); -}; diff --git a/src/routes/(app)/[username]/invoice/[id]/+layout.server.ts b/src/routes/(app)/[username]/invoice/[id]/+layout.server.ts deleted file mode 100644 index 41554c9f..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/+layout.server.ts +++ /dev/null @@ -1,39 +0,0 @@ -import Qr from "qrcode-base64"; -import { get } from "$lib/utils"; -import { redirect } from "@sveltejs/kit"; - -export async function load({ depends, params, url, parent }) { - depends("app:invoice"); - - let { subject, user } = await parent(); - let { id } = params; - let invoice = await get(`/invoice/${id}`); - let options = !!url.searchParams.get("options"); - - if ( - user && - invoice.uid !== user.id && - !(url.pathname.includes("tip") || url.pathname.includes("memo")) && - !options - ) { - redirect(307, `/send/invoice/${id}`); - } - - let { amount, pending, received } = invoice; - amount = parseInt(amount); - - let paid = - (!amount && (pending || received)) || - (amount > 0 && (pending >= amount || received >= amount)); - if (paid && !url.pathname.endsWith("paid")) { - redirect( - 307, - `/${params.username}/invoice/${id}/paid` + - (options ? "?options=true" : "") - ); - } - - let src = Qr.drawImg(invoice.text || "", { size: 340 }); - - return { id, invoice, subject, src }; -} diff --git a/src/routes/(app)/[username]/invoice/[id]/+layout.svelte b/src/routes/(app)/[username]/invoice/[id]/+layout.svelte deleted file mode 100644 index 4fa864ce..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/+layout.svelte +++ /dev/null @@ -1 +0,0 @@ -<slot /> diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.server.ts b/src/routes/(app)/[username]/invoice/[id]/+page.server.ts deleted file mode 100644 index a57efa64..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/+page.server.ts +++ /dev/null @@ -1,5 +0,0 @@ -import invoice from "$lib/invoice"; - -export const actions = { - default: invoice, -}; diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/+page.svelte deleted file mode 100644 index 1f2bbb51..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/+page.svelte +++ /dev/null @@ -1,344 +0,0 @@ -<script> - import { enhance } from "$app/forms"; - import { send } from "$lib/socket"; - import { - btc, - post, - back, - copy, - f, - get, - types, - sat, - reverseFormat, - s, - sats, - } from "$lib/utils"; - import { tick, onMount, onDestroy } from "svelte"; - import { browser } from "$app/environment"; - import { last, showQr, amountPrompt } from "$lib/store"; - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import Heart from "$comp/Heart.svelte"; - import Image from "$comp/Image.svelte"; - import Numpad from "$comp/Numpad.svelte"; - import { t } from "$lib/translations"; - import { goto, invalidate } from "$app/navigation"; - import Toggle from "$comp/Toggle.svelte"; - - export let data; - - let showOptions; - - let reading = async ({ message, serialNumber }) => { - let name = serialNumber.replace(/:/g, "-"); - let result = await post(`/fund/${name}/withdraw`, { amount, hash, name }); - }; - - let readingerror = (e) => console.log("nfc error", e); - - $: refresh(data); - let { invoice, id, subject, user, src } = data; - let { - account, - amount, - hash, - type, - rate, - text, - tip, - user: { username, currency }, - } = invoice; - - let qr; - let tipPercent = 0; - - let refresh = async (data) => { - ({ invoice, id, user, src } = data); - ({ - account, - amount, - hash, - type, - rate, - text, - tip, - user: { username, currency }, - } = invoice); - - tipPercent = (tip / amount) * 100; - }; - - $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2)); - $: tipAmount = ((tip * rate) / sats).toFixed(2); - - let subbed; - - onMount(async () => { - if (browser) { - last.subscribe((v) => { - if (!v || subbed) return; - subbed = true; - send("subscribe", invoice); - }); - - if ($amountPrompt && !tip) toggleAmount(); - } - }); - - $: link = [types.bitcoin, types.liquid].includes(type) - ? text - : `lightning:${text}`; - $: txt = [types.bitcoin, types.liquid].includes(type) ? hash : text; - - let setType = async (type) => { - invoice.type = type; - ({ id } = await post(`/${username}/invoice`, { - invoice, - user: { username, currency }, - })); - - goto(`./${id}?options=true`, { invalidateAll: true, noScroll: true }); - }; - - let setAmount = async () => { - if (typeof $amountPrompt === "undefined") $amountPrompt = true; - settingAmount = false; - amount = newAmount; - invoice.amount = newAmount; - invoice.tip = 0; - ({ id } = await post(`/${username}/invoice`, { - invoice, - user: { username, currency }, - })); - - let url = `./${id}`; - if (subject?.prompt) url += "/tip"; - else url += "?options=true"; - - goto(url, { invalidateAll: true, noScroll: true }); - }; - - let newAmount; - let settingAmount; - let toggleAmount = () => (settingAmount = !settingAmount); - let fiat; - let submit; -</script> - -<div class="invoice container mx-auto max-w-xl px-4 space-y-2"> - {#if $showQr} - <div> - <a - href={[types.bitcoin, types.liquid].includes(invoice.type) - ? invoice.text - : "lightning:" + invoice.text} - > - <img - {src} - class="mx-auto z-10 shadow-inner rounded-2xl" - bind:this={qr} - alt={txt} - /> - </a> - </div> - {:else} - <div class="break-all pb-5 text-lg text-center"> - {txt} - </div> - {/if} - - {#each invoice.items as i} - <div class="grid grid-cols-12 text-xl"> - <div class="col-span-1 my-auto">{i.quantity}</div> - <div class="mr-auto grow col-span-7 my-auto"> - {i.name} - </div> - <div class="col-span-2 font-semibold text-right my-auto"> - {f(i.price * i.quantity, currency)} - </div> - <div class="col-span-2 text-secondary text-right text-lg my-auto"> - {sat(btc(i.price * i.quantity, rate))} - </div> - </div> - {/each} - - {#if amount > 0} - <div class="text-center font-bold text-2xl"> - <div> - {f(amountFiat, currency)} - - {#if tip} - <span class="text-sm"> - +{f(tip * (rate / sats), currency)} - </span> - {/if} - </div> - <div> - <span class="text-secondary font-normal text-xl" - >⚡️{`${s(amount)}`}</span - > - - {#if tip} - <span class="text-sm text-secondary font-normal"> - +⚡️{s(tip)} - </span> - {/if} - </div> - </div> - {/if} - - <div class="text-secondary space-y-2 text-xl"> - <button - type="button" - class="flex gap-2 text-center break-all rounded-2xl hover:opacity-80 py-5 px-6 w-full mx-auto justify-center border whitespace-nowrap mb-2" - on:click={() => copy(txt)} - > - <Icon icon="copy" style="w-8 my-auto" /> - <div class="my-auto"> - {$t("payments.copy")} - </div> - </button> - - {#if user?.id === invoice?.user?.id} - <button - class="w-full flex justify-center rounded-2xl border py-5 px-6 hover:opacity-80" - on:click={toggleAmount} - > - <Icon icon="edit" style="mr-1 w-8" /> - <div class="my-auto">{$t("payments.setAmount")}</div> - </button> - {:else} - <a href={link} class="w-full"> - <button - class="w-full flex justify-center rounded-2xl border py-5 px-6 hover:opacity-80" - > - <Icon icon="mobile" style="mr-1 w-8" /> - <div class="my-auto">{$t("payments.openLink")}</div> - </button> - </a> - {/if} - - <div class="w-full flex justify-center gap-2 flex-wrap"> - <button - class="w-full flex justify-center rounded-2xl border py-5 px-6 hover:opacity-80" - on:click={() => ($showQr = !$showQr)} - > - {#if $showQr} - <Icon icon="text" style="w-6 mr-2 my-auto" /> - {:else} - <Icon icon="qr" style="w-8 mr-1 invert my-auto" /> - {/if} - <div class="my-auto"> - {$showQr ? $t("payments.showText") : $t("payments.showQr")} - </div></button - > - </div> - </div> - - {#if !account} - <div class="flex justify-around text-secondary"> - <button - class="hover:bg-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" - class:bg-primary={type === types.bitcoin} - class:text-black={type === types.bitcoin} - on:click={() => setType(types.bitcoin)} - > - <img src="/images/bitcoin.svg" class="w-8" alt="Bitcoin" /> - <div class="my-auto text-lg">Bitcoin</div> - </button> - - <button - class="hover:bg-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" - class:bg-primary={type === types.liquid} - class:text-black={type === types.liquid} - on:click={() => setType(types.liquid)} - > - <img src="/images/liquid.svg" class="w-8" alt="Liquid" /> - <div class="my-auto text-lg">Liquid</div> - </button> - - <button - class="hover:bg-primary flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" - class:bg-primary={type === types.lightning} - class:text-black={type === types.lightning} - on:click={() => setType(types.lightning)} - > - <div class="bg-white rounded-full w-8 h-8 text-center flex"> - <div class="m-auto">⚡️</div> - </div> - <div class="my-auto text-lg">Lightning</div> - </button> - </div> - {/if} - - {#if type === types.liquid} - <div class="flex justify-center"> - <div class="my-auto text-xl text-center text-secondary"> - {$t("payments.onlyLbtc")} - <span class="text-teal-500 font-bold">L-BTC</span>, - {$t("payments.dontDeposit")} - </div> - </div> - {/if} - - {#if user?.id === invoice?.user?.id} - <a href={`/receive`} class="block"> - <button - class="flex gap-2 text-center break-all rounded-2xl hover:opacity-80 py-5 px-6 w-full mx-auto justify-center border text-xl whitespace-nowrap text-secondary" - > - <Icon icon="voucher" style="w-8 my-auto" /> - <div class="my-auto"> - {$t("payments.redeemVoucher")} - </div></button - > - </a> - {/if} -</div> - - <!-- <div --> - <!-- class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20" --> - <!-- > --> - <!-- <div --> - <!-- class="relative top-1/3 mx-auto p-12 border w-96 shadow-lg rounded-md bg-white space-y-5" --> - <!-- > --> - <!-- <form submit={setAmount}> --> - <!-- <Numpad --> - <!-- bind:amount={newAmount} --> - <!-- bind:currency --> - <!-- bind:rate --> - <!-- bind:fiat --> - <!-- bind:submit --> - <!-- /> --> - <!-- <div class="w-full flex flex-wrap gap-2"> --> - <!-- <button --> - <!-- type="submit" --> - <!-- on:click={setAmount} --> - <!-- class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto bg-black text-white w-full" --> - <!-- > --> - <!-- <div class="my-auto">Ok</div> --> - <!-- </button> --> - <!-- <button --> - <!-- bind:this={submit} --> - <!-- type="button" --> - <!-- class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto w-full" --> - <!-- on:click={toggleAmount} --> - <!-- on:keydown={toggleAmount} --> - <!-- > --> - <!-- <div class="my-auto">Cancel</div> --> - <!-- </button> --> - <!-- </div> --> - <!-- </form> --> - <!-- --> - <!-- <div class="flex justify-between items-center"> --> - <!-- <span class="font-bold">{$t("payments.amountPrompt")}</span> --> - <!-- <Toggle id="notify" bind:value={$amountPrompt} /> --> - <!-- </div> --> - <!-- </div> --> - <!-- </div> --> - -<style> - .invoice { - view-transition-name: invoice; - } -</style> diff --git a/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.ts b/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.ts deleted file mode 100644 index 58d56018..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import invoice from "$lib/invoice"; -import { redirect } from "@sveltejs/kit"; - -export async function load({ parent, params }) { - let { invoice } = await parent(); - if (!invoice.amount) { - redirect(307, `/${params.username}/invoice/${invoice.id}`); - } -} - -export const actions = { - default: invoice, -}; diff --git a/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte deleted file mode 100644 index 4d23ff19..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte +++ /dev/null @@ -1,95 +0,0 @@ -<script> - import { onMount } from "svelte"; - import { browser } from "$app/environment"; - import { invoice as inv, request } from "$lib/store"; - import { - copy, - select, - f, - sat, - get, - reverseFormat, - s, - sats, - } from "$lib/utils"; - import Icon from "$comp/Icon.svelte"; - import { t } from "$lib/translations"; - import { enhance } from "$app/forms"; - - let submitting; - let submit; - - export let data; - $: refresh(data); - let { invoice, id, user, rates } = data; - let { - amount, - hash, - items, - memoPrompt, - type, - rate, - received, - prompt, - text, - tip, - user: { username, currency }, - } = invoice; - - invoice.memo = user?.username; - - let refresh = (data) => { - $inv = null; - - ({ invoice, id } = data); - ({ - amount, - hash, - items, - memoPrompt, - type, - received, - prompt, - text, - tip, - user: { username }, - } = invoice); - }; -</script> - -<form - method="POST" - use:enhance - class:invisible={submitting} - class="container px-4 max-w-lg mx-auto space-y-5" -> - <input type="hidden" name="amount" value={amount} /> - <input type="hidden" name="tip" value={tip} /> - <input type="hidden" name="username" value={username} /> - <input type="hidden" name="rate" value={invoice.rate} /> - <input type="hidden" name="memoPrompt" value={invoice.memoPrompt} /> - <input type="hidden" name="prompt" value={invoice.prompt} /> - <input type="hidden" name="type" value={type} /> - <input type="hidden" name="hash" value={hash} /> - <input type="hidden" name="items" value={JSON.stringify(items)} /> - - <h1 class="text-4xl font-semibold text-center">{$t("invoice.addMemo")}</h1> - <div class="space-y-2"> - <input - use:select - name="memo" - class="w-full p-4 border rounded-xl text-xl" - bind:value={invoice.memo} - autocapitalize="none" - /> - <div class="text-secondary">{invoice.memoPrompt}</div> - </div> - - <button - type="submit" - bind:this={submit} - class="bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold" - > - {$t("payments.next")} - </button> -</form> diff --git a/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte deleted file mode 100644 index 700ac972..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte +++ /dev/null @@ -1,85 +0,0 @@ -<script> - import { scale } from "svelte/transition"; - import Icon from "$comp/Icon.svelte"; - import { toast } from "@zerodevx/svelte-toast"; - import { browser } from "$app/environment"; - import { t } from "$lib/translations"; - import { fiat, f, s, sat, sats } from "$lib/utils"; - - export let data; - let amount, currency, rate, received, pending, tip, user, id; - $: update(data); - let update = (data) => { - ({ amount, currency, rate, received, pending, tip, user, id } = - data.invoice); - }; - - toast.pop(0); -</script> - -<div class="container px-4 text-center mx-auto"> - {#if received} - <div - class="flex w-full py-5 max-w-[200px] mx-auto" - in:scale={{ start: 0.5 }} - > - <Icon icon="check" style="mx-auto" /> - </div> - - <h1 class="text-3xl md:text-4xl font-bold mb-6"> - {$t("invoice.paymentSuccessful")} - </h1> - - <h2 class="text-2xl md:text-3xl font-semibold"> - {f(fiat(received - tip, rate), currency)} - {#if tip} - <span class="text-lg"> - + {f(fiat(tip, rate), currency)} - </span> - {/if} - </h2> - <h3 class="text-secondary md:text-lg mb-6 mt-1"> - ⚡️{s(received - tip)} - - {#if tip} - <span class="text-lg"> - + ⚡️{s(tip)} - </span> - {/if} - </h3> - {/if} - - {#if pending} - <div - class="flex w-full py-5 max-w-[200px] mx-auto" - in:scale={{ start: 0.5 }} - > - <Icon icon="orangeclock" style="mx-auto" /> - </div> - - <h1 class="text-3xl md:text-4xl font-bold mb-6">Payment detected</h1> - <h2 class="text-2xl md:text-3xl font-semibold"> - {f(fiat(pending - tip, rate), currency)} - {#if tip} - + {f(fiat(tip, rate), currency)} - {/if} - </h2> - - <h3 class="text-secondary md:text-lg mb-6 mt-1"> - ⚡️{s(pending - tip)} - {#if tip} - <span class="text-lg"> - + ⚡️{s(tip)} - </span> - {/if} - </h3> - {/if} -</div> - -<a href={`/${user.username}/receive`}> - <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" /> -</a> - -<div class="fixed bottom-10 w-full text-center"> - {$t("payments.tapAnywhere")} -</div> diff --git a/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.ts b/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.ts deleted file mode 100644 index b81e2364..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import invoice from "$lib/invoice"; -import { redirect } from "@sveltejs/kit"; - -export async function load({ parent, params }) { - let { invoice } = await parent(); - if (!invoice.amount) - redirect(307, `/${params.username}/invoice/${invoice.id}`); -} - -export const actions = { - default: invoice, -}; diff --git a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte deleted file mode 100644 index 35b24bf1..00000000 --- a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte +++ /dev/null @@ -1,209 +0,0 @@ -<script> - import { onMount } from "svelte"; - import { browser } from "$app/environment"; - import { invoice as inv, request } from "$lib/store"; - import { copy, focus, f, sat, get, reverseFormat, s, sats } from "$lib/utils"; - import Icon from "$comp/Icon.svelte"; - import Slider from "$comp/Slider.svelte"; - import { goto } from "$app/navigation"; - import { t } from "$lib/translations"; - import { enhance } from "$app/forms"; - import { tick } from "svelte"; - - const tipAmounts = ["No", "10%", "15%", "20%"]; - let showCustomAmount; - - let customInput; - let customTipAmount; - - let apply = async () => { - if (customTipAmount > 0) { - tipPercent = (customTipAmount / amountFiat) * 100; - showCustomAmount = false; - } - }; - - let active = (amount) => - !customTipAmount && Math.round(tipPercent) === parseInt(amount.slice(0, 2)); - - const handleCustomTipAmount = (e) => { - tipPercent = 0; - customTipAmount = e.target.value; - }; - - let timeout; - const handleSlide = (e) => { - customTipAmount = ""; - if (customInput) { - customInput.value = ""; - } - tipPercent = e.target.value; - }; - - let submitting; - const handleTipButtonClick = async (amount) => { - submitting = true; - if (amount === "No") { - tipPercent = 0; - } else { - tipPercent = parseInt(amount.slice(0, 2)); - } - - await tick(); - submit.click(); - }; - - let submit; - - export let data; - $: refresh(data); - let { invoice, id, user, rates } = data; - let { - amount, - hash, - items, - type, - rate, - received, - prompt, - text, - tip, - user: { username, currency }, - } = invoice; - - let qr; - let tipPercent = 0; - - let fullscreen; - - let refresh = (data) => { - $inv = null; - - ({ invoice, id } = data); - ({ - amount, - hash, - items, - type, - received, - prompt, - text, - tip, - user: { username }, - } = invoice); - - tipPercent = (tip / amount) * 100; - }; - - $: currency = user?.currency || invoice.currency; - $: rate = invoice.rate * (rates[currency] / rates[invoice.currency]); - - $: tip = Math.round((amount / 100) * tipPercent); - - $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2)); - - $: tipAmount = ((tip * rate) / sats).toFixed(2); - - $: invoiceAmountFiatFormatted = f(amountFiat, currency); - - onMount(() => ($request = undefined)); -</script> - -<div class="container px-4 max-w-lg text-center mx-auto"> - <form method="POST" use:enhance class:invisible={submitting}> - <input type="hidden" name="amount" value={amount} /> - <input type="hidden" name="tip" value={tip} /> - <input type="hidden" name="username" value={username} /> - <input type="hidden" name="rate" value={invoice.rate} /> - <input type="hidden" name="prompt" value="false" /> - <input type="hidden" name="type" value={type} /> - <input type="hidden" name="hash" value={hash} /> - <input type="hidden" name="items" value={JSON.stringify(items)} /> - - {#if invoice.memoPrompt} - <input type="hidden" name="memoPrompt" value={invoice.memoPrompt} /> - {/if} - - <h1 class="text-4xl font-semibold my-8">{$t("invoice.addTipq")}</h1> - {#if !showCustomAmount} - <div class="my-2 text-xl">{Math.round(tipPercent)}%</div> - - <Slider handle={handleSlide} bind:value={tipPercent} /> - - <div class="flex mb-8 text-lg"> - <div> - <span class="mr-1"> - {f(amountFiat, currency)} - <span class="font-semibold">+{f(tipAmount, currency)}</span> - </span> - </div> - - <div class="ml-auto text-lg"> - {sat(amount)} - <span class="font-semibold">+{sat(tip)}</span> - </div> - </div> - - {#if tip} - <div> - <button type="button" on:click={() => submit.click()}>Next</button> - <button type="button" on:click={() => (tipPercent = 0)}>Reset</button> - </div> - {:else} - {#each tipAmounts as amount} - <button - type="button" - class:active={active(amount, tipPercent)} - on:click={() => handleTipButtonClick(amount)}>{amount}</button - > - {/each} - {/if} - - <div> - <button type="button" on:click={() => (showCustomAmount = true)} - >Custom</button - > - </div> - {/if} - - <button type="submit" bind:this={submit} /> - </form> - - {#if showCustomAmount} - <form on:submit|preventDefault={apply}> - <div class="relative"> - <span - class="absolute top-[18px] left-5 font-semibold {!customTipAmount - ? 'opacity-50' - : 'opacity-100'}">$</span - > - <input - bind:this={customInput} - type="number" - step="0.01" - on:input={(e) => handleCustomTipAmount(e)} - class="pl-10" - placeholder="Custom amount" - use:focus - /> - <button - type="submit" - class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount - ? 'opacity-50' - : 'opacity-100 hover:opacity-80'}" - disabled={!customTipAmount}>Apply</button - > - </div> - </form> - {/if} -</div> - -<style> - .active { - @apply !bg-black !text-white; - } - - .container button[type="button"] { - @apply w-full mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-5 px-7 rounded-2xl; - } -</style> diff --git a/src/routes/(app)/[username]/invoice/sent/+page.svelte b/src/routes/(app)/[username]/invoice/sent/+page.svelte deleted file mode 100644 index b95bc85e..00000000 --- a/src/routes/(app)/[username]/invoice/sent/+page.svelte +++ /dev/null @@ -1,11 +0,0 @@ -<script> - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import { request } from "$lib/store"; -</script> - -Invoice sent to -<div class="flex"> - <Avatar user={$request.requester} size={12} /> - <h1 class="my-auto"><b>{$request.requester.username}</b> is ready to pay</h1> -</div> diff --git a/src/routes/(app)/[username]/items/[id]/+page.server.ts b/src/routes/(app)/[username]/items/[id]/+page.server.ts deleted file mode 100644 index 67cebd93..00000000 --- a/src/routes/(app)/[username]/items/[id]/+page.server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { fd, auth, get, post } from "$lib/utils"; -import { fail, redirect } from "@sveltejs/kit"; - -export let load = async ({ params: { id } }) => { - let item = await get(`/items/${id}`); - return { item }; -}; - -export const actions = { - default: async ({ cookies, request, url }) => { - let form = await fd(request); - try { - await post(`/items`, form, auth(cookies)); - } catch (e: any) { - return fail(400, { message: e.message }); - } - - let success = `/${url.pathname.split("/")[1]}`; - redirect(307, success); - }, -}; diff --git a/src/routes/(app)/[username]/items/[id]/+page.svelte b/src/routes/(app)/[username]/items/[id]/+page.svelte deleted file mode 100644 index 65720350..00000000 --- a/src/routes/(app)/[username]/items/[id]/+page.svelte +++ /dev/null @@ -1,18 +0,0 @@ -<script> - import { t } from "$lib/translations"; - import ItemForm from "$comp/ItemForm.svelte"; - import { fail } from "$lib/utils"; - - export let data; - export let form; - - $: form?.message && fail(form.message); - let { item, user } = data; -</script> - -<div class="container max-w-xl mx-auto px-4"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> - {$t("items.edit")} - </h1> - <ItemForm {item} {user} /> -</div> diff --git a/src/routes/(app)/[username]/items/create/+page.server.ts b/src/routes/(app)/[username]/items/create/+page.server.ts deleted file mode 100644 index 2e006a3e..00000000 --- a/src/routes/(app)/[username]/items/create/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { fail, redirect } from "@sveltejs/kit"; -import { fd, auth, post } from "$lib/utils"; - -export const actions = { - default: async ({ cookies, request, url }) => { - let form = await fd(request); - try { - await post("/items", form, auth(cookies)); - } catch (e: any) { - return fail(400, { message: e.message }); - } - - let success = `/${url.pathname.split("/")[1]}`; - redirect(307, success); - }, -}; diff --git a/src/routes/(app)/[username]/items/create/+page.svelte b/src/routes/(app)/[username]/items/create/+page.svelte deleted file mode 100644 index b82889c9..00000000 --- a/src/routes/(app)/[username]/items/create/+page.svelte +++ /dev/null @@ -1,19 +0,0 @@ -<script> - import { t } from "$lib/translations"; - import ItemForm from "$comp/ItemForm.svelte"; - import { fail } from "$lib/utils"; - - export let data; - export let form; - - $: form?.message && fail(form.message); - let { user } = data; - let item = {}; -</script> - -<div class="container max-w-xl mx-auto px-4"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> - {$t("items.add")} - </h1> - <ItemForm {item} {user} /> -</div> diff --git a/src/routes/(app)/[username]/map/+page.server.ts b/src/routes/(app)/[username]/map/+page.server.ts deleted file mode 100644 index 616d28f6..00000000 --- a/src/routes/(app)/[username]/map/+page.server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { get } from "$lib/utils"; - -// export const prerender = true; -export let load = async () => { - let data = { locations: [] }; - - try { - data = await get("/locations"); - } catch (e) {} - - return data; -}; diff --git a/src/routes/(app)/[username]/map/+page.svelte b/src/routes/(app)/[username]/map/+page.svelte deleted file mode 100644 index bfa115e4..00000000 --- a/src/routes/(app)/[username]/map/+page.svelte +++ /dev/null @@ -1,8 +0,0 @@ -<script> - import LocationsMap from "$comp/LocationsMap.svelte"; - export let data; - - let { locations } = data; -</script> - -<LocationsMap {locations} /> diff --git a/src/routes/(app)/[username]/messages/+page.server.ts b/src/routes/(app)/[username]/messages/+page.server.ts deleted file mode 100644 index 94659621..00000000 --- a/src/routes/(app)/[username]/messages/+page.server.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { get } from "$lib/utils"; -import { redirect } from "@sveltejs/kit"; - -interface Message { - created_at: number; -} - -interface Note { - created_at: number; - seen: number; -} - -export async function load({ params, parent, }) { - let { user } = await parent(); - if (user.username !== params.username) redirect(307, `/${params.username}`); - let { since = 0 } = params; - - let messages = [], - notes = []; - - if (user?.pubkey) { - let { pubkey } = user; - try { - messages = await get(`/${pubkey}/${since}/messages`); - messages = messages.sort((a: Message, b: Message) => b.created_at - a.created_at); - } catch (e) { - console.log(`failed to fetch nostr messages`, e); - } - - try { - notes = await get(`/${pubkey}/notes`); - } catch (e) { - console.log(`failed to fetch nostr notes for ${pubkey}`, e); - } - - notes.map((e: Note) => { - e.seen = e.created_at; - }); - } - - return { messages, notes }; -} diff --git a/src/routes/(app)/[username]/messages/+page.svelte b/src/routes/(app)/[username]/messages/+page.svelte deleted file mode 100644 index 4738ea2c..00000000 --- a/src/routes/(app)/[username]/messages/+page.svelte +++ /dev/null @@ -1,92 +0,0 @@ -<script> - import { t } from "$lib/translations"; - import Avatar from "$comp/Avatar.svelte"; - import { event as e, password } from "$lib/store"; - import { browser } from "$app/environment"; - import { decrypt } from "$lib/nostr"; - - export let data; - - let { messages, notes, invoices, sent, received, subject, user, rates } = - data; - $: refresh(data); - let refresh = (d) => - ({ messages, notes, invoices, sent, received, subject, user, rates } = d); - - let keys = new Set(); - let latest = []; - let ready; - - e.subscribe(async (event) => { - if (!(browser && event && ready)) return; - if ( - event.recipient?.id === user.id && - !~latest.findIndex((m) => m.id === event.id) - ) { - event.content = await decrypt({ event, user }); - - let i = latest.findIndex((m) => m.pubkey === event.pubkey); - let popped; - if (~i) popped = latest.splice(i, 1); - else popped = latest.pop(); - - latest.unshift(event); - latest = latest; - } - }); - - $: initialize($password); - let initialize = async (p) => { - let i = 0; - ready = false; - if (!(browser && user)) return; - while (i < messages.length) { - let event = messages[i]; - i++; - - if (!(event.author && event.recipient)) continue; - - let k = - event.author.id === user.id - ? event.recipient?.pubkey - : event.author.pubkey; - - if (!keys.has(k)) { - keys.add(k); - event.content = await decrypt({ event, user }); - if (event.content) latest.push(event); - } - } - - latest = latest; - ready = true; - }; -</script> - -<div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> - {$t("user.messages")} - </h1> - {#if latest.length && user?.id === subject.id} - <div class="relative"> - {#each latest as { content, pubkey, author, recipient }} - {@const user = author.id === user.id ? recipient : author} - <a href={`/${subject.username}/messages/${user.username}`}> - <div class="flex hover:bg-gray-100 p-4 rounded-2xl"> - <div class="my-auto"> - <Avatar {user} size={"20"} disabled={true} /> - </div> - <div class="my-auto truncate"> - <div class="my-auto ml-1 text-lg font-bold">{user.username}</div> - <div class="my-auto ml-1 text-secondary text-lg truncate"> - {content} - </div> - </div> - </div> - </a> - {/each} - </div> - {:else} - <div class="text-center">{$t("user.noMessages")}</div> - {/if} -</div> diff --git a/src/routes/(app)/[username]/messages/[recipient]/+page.server.ts b/src/routes/(app)/[username]/messages/[recipient]/+page.server.ts deleted file mode 100644 index 6be9f6ec..00000000 --- a/src/routes/(app)/[username]/messages/[recipient]/+page.server.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { get } from "$lib/utils"; -import type { Message } from "$lib/types"; - -export async function load({ params, parent }) { - let { user } = await parent(); - let { since = 0 } = params; - - let messages: Message[] = []; - let recipient = await get(`/users/${params.recipient}`); - - try { - messages = await get(`/${user.pubkey}/${since}/messages`); - - messages = messages - .filter( - (m: Message) => m.recipient?.id === recipient.id || m.author?.id === recipient.id - ) - .sort((a, b) => a.created_at - b.created_at); - } catch (e) { - console.log(`failed to fetch nostr messages`, e); - } - - return { messages, recipient, user }; -} diff --git a/src/routes/(app)/[username]/messages/[recipient]/+page.svelte b/src/routes/(app)/[username]/messages/[recipient]/+page.svelte deleted file mode 100644 index 98638389..00000000 --- a/src/routes/(app)/[username]/messages/[recipient]/+page.svelte +++ /dev/null @@ -1,191 +0,0 @@ -<script> - import { cubicInOut } from "svelte/easing"; - import { browser } from "$app/environment"; - import { t } from "$lib/translations"; - import { format } from "date-fns"; - import { scale, fade, fly } from "svelte/transition"; - import { enhance } from "$app/forms"; - import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import { back, fail, focus } from "$lib/utils"; - import { sign, send, encrypt, decrypt } from "$lib/nostr"; - import { event as e, password } from "$lib/store"; - import { tick, onMount } from "svelte"; - import { getEventHash } from "nostr-tools"; - - export let data; - - function fadeScale( - node, - { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 } - ) { - const o = +getComputedStyle(node).opacity; - const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/); - const s = m ? m[1] : 1; - const is = 1 - baseScale; - - return { - delay, - duration, - css: (t) => { - const eased = easing(t); - return `opacity: ${eased * o}; transform: scale(${ - eased * s * is + baseScale - })`; - }, - }; - } - - let { messages, recipient, user } = data; - let input, pane; - - $: initialize($password); - let initialize = async (p) => { - await Promise.all( - messages.map( - async (event) => (event.message = await decrypt({ event, user })) - ) - ); - - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); - }; - - e.subscribe(async (event) => { - let found = ~messages.findIndex((m) => m.id === event?.id); - if (event?.recipient.id === user.id && !found) { - event.message = await decrypt({ event, user }); - messages.push(event); - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); - } - }); - - let sent, submitting, message; - let submit = async () => { - submitting = true; - - let event = { - pubkey: user.pubkey, - created_at: Math.floor(Date.now() / 1000), - kind: 4, - tags: [["p", recipient.pubkey]], - }; - - event.message = message; - - message = ""; - - event.author = user; - event.recipient = recipient; - - messages.push(event); - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); - - try { - event.content = await encrypt({ - message: event.message, - recipient: recipient.pubkey, - user, - }); - event.id = getEventHash(event); - await sign({ event, user }); - await send(event); - - sent = true; - } catch (e) { - console.log(e); - fail("Failed to send message"); - } - - message = ""; - submitting = false; - }; - - let keydown = (e) => { - if (e.key === "Enter") { - e.preventDefault(); - submit(); - } - }; -</script> - -<div class="container max-w-xl mx-auto px-4 space-y-5"> - <button - type="button" - class="hover:opacity-80" - data-sveltekit-preload-data="false" - on:click={back} - > - <Icon icon="arrow-left" style="w-10" /> - </button> - - <div - class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8" - bind:this={pane} - > - {#each messages as { id, author, message, created_at, pubkey }} - {@const ours = pubkey === user.pubkey} - {@const theirs = !ours} - - {#if message} - <div - class="flex gap-2" - class:flex-row-reverse={theirs} - class:justify-end={theirs} - in:fadeScale={{ - easing: cubicInOut, - baseScale: 0.5, - }} - > - <div - class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg" - class:ours - class:theirs - :key={id} - > - {message} - </div> - <div class="mt-auto"> - <Avatar user={author} size={"12"} /> - </div> - </div> - <div class="text-sm text-gray-400 mb-6" class:text-right={ours}> - {format(new Date(created_at * 1000), "MMM d")}, - {format(new Date(created_at * 1000), "h:mm aa")} - </div> - {/if} - {/each} - </div> - - <form - method="POST" - class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2" - on:submit|preventDefault={submit} - > - <input type="hidden" name="requester_id" value={user.id} /> - <input type="hidden" name="recipient" value={recipient.username} /> - - <div - use:focus - contenteditable - class="grow break-all py-4 outline-none mt-0 pl-4" - bind:innerHTML={message} - on:keydown={keydown} - /> - <button type="submit" class="my-auto shrink-0"> - <Icon icon="send" style="w-8" /> - </button> - </form> -</div> - -<style> - .ours { - @apply bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] text-black rounded-br-none ml-auto; - } - - .theirs { - @apply bg-gray-100 text-black rounded-bl-none; - } -</style> diff --git a/src/routes/(app)/[username]/reset/+server.ts b/src/routes/(app)/[username]/reset/+server.ts deleted file mode 100644 index f7dba02c..00000000 --- a/src/routes/(app)/[username]/reset/+server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request, params }) { - let { username } = params; - let r = await request.json(); - let { password } = r; - let result = await post("/reset", { password, username }, auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.server.ts b/src/routes/(app)/[username]/settings/[...tab]/+page.server.ts deleted file mode 100644 index 42639b38..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/+page.server.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { fail, redirect } from "@sveltejs/kit"; -import { fd, auth, get, post } from "$lib/utils"; - -export function load({ cookies, params, url }) { - if (url.pathname.endsWith("settings")) - redirect(307, url.pathname + "/account"); - params.cookies = cookies.getAll(); - return { cookies: cookies.getAll(), tab: params.tab }; -} - -export const actions = { - default: async ({ cookies, request }) => { - let form = await fd(request); - - if (form.tab === "pos") { - form.notify = form.notify === "on"; - form.nip5 = form.nip5 === "on"; - form.prompt = form.prompt === "on"; - form.autowithdraw = form.autowithdraw === "on"; - } - - let user = { ...(await get("/me", auth(cookies))), ...form }; - - try { - ({ user } = await post(`/user`, user, auth(cookies))); - } catch (e: any) { - return fail(400, { message: e.message }); - } - - if (user.language) cookies.set("lang", user.language, { path: "/" }); - - return { user, success: true }; - }, -}; diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte deleted file mode 100644 index 12980291..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte +++ /dev/null @@ -1,257 +0,0 @@ -<script> - import { browser } from "$app/environment"; - import { onMount, tick } from "svelte"; - import { fly } from "svelte/transition"; - import { applyAction, deserialize } from "$app/forms"; - - import Icon from "$comp/Icon.svelte"; - import Spinner from "$comp/Spinner.svelte"; - import Pin from "$comp/Pin.svelte"; - import { loading, t } from "$lib/translations"; - import { fail, auth, post, sleep, warning, success } from "$lib/utils"; - import { avatar, banner, password, pin } from "$lib/store"; - import { upload } from "$lib/upload"; - import { page } from "$app/stores"; - import { sign, send, reEncryptEntropy, setNsec } from "$lib/nostr"; - import { invalidateAll } from "$app/navigation"; - - import Account from "./_account.svelte"; - import Pos from "./_pos.svelte"; - import Security from "./_security.svelte"; - import Shopify from "./_shopify.svelte"; - - import { PUBLIC_COINOS_URL } from "$env/static/public"; - - export let data; - export let form; - - let submit; - let formElement; - - let { user, token, rates, tab, cookies } = data; - let prev; - - $: update(data); - let update = () => { - ({ user, token, rates, tab } = data); - prev = { ...user }; - }; - - $: form?.user && ({ user } = form); - - $: form?.success && throttledSuccess(); - - let justUpdated; - let throttledSuccess = () => { - if (!justUpdated) { - justUpdated = true; - success($t("user.settings.saved"), false); - setTimeout(() => (justUpdated = false), 5000); - } - }; - - $: form?.message && fail(form.message); - - $: if (form?.message?.startsWith("Pin")) { - fail("Wrong pin, try again"); - $pin = ""; - } - - let tabs = [ - { name: "account", key: "ACCOUNT", comp: Account }, - { name: "pos", key: "POINT_OF_SALE", comp: Pos }, - { name: "security", key: "SECURITY", comp: Security }, - { name: "shopify", key: "SHOPIFY", comp: Shopify }, - ]; - - $: ({ comp } = tabs.find((t) => t.name === tab)); - - let { address, id, username } = user; - let submitting; - - $: if (!$loading && $page.url.searchParams.get("verified")) - success($t("user.settings.verified")); - - async function handleSubmit() { - try { - submitting = true; - let data = new FormData(formElement); - - if (data.get("newNsec")) { - await setNsec(user, data.get("newNsec")); - data.set("nsec", user.nsec); - data.set("pubkey", user.pubkey); - } - - if (data.get("password")) { - try { - data.set( - "cipher", - await reEncryptEntropy(user, data.get("password")) - ); - } catch (e) { - console.log("Failed to encrypt keys with new password"); - throw e; - } - } - - if ($avatar) { - try { - let { hash } = JSON.parse( - await upload($avatar.file, $avatar.type, $avatar.progress, token) - ); - - data.set("profile", hash); - - await fetch(`/api/public/${hash}.webp`, { - cache: "reload", - mode: "no-cors", - }); - } catch (e) { - console.log("problem upsubmitting avatar", e); - } - } - - if ($banner) { - try { - let { hash } = JSON.parse( - await upload($banner.file, $banner.type, $banner.progress, token) - ); - - data.set("banner", hash); - - await fetch(`/api/public/${hash}.webp`, { - cache: "reload", - mode: "no-cors", - }); - } catch (e) { - console.log("problem upsubmitting banner", e); - } - } - - if (["username", "address", "profile"].some((a) => user[a] !== prev[a])) { - let event = { - pubkey: user.pubkey, - created_at: Math.floor(Date.now() / 1000), - kind: 0, - content: JSON.stringify({ - name: user.username, - about: user.address, - picture: `${PUBLIC_COINOS_URL}/public/${user.profile}.webp`, - }), - tags: [], - }; - - try { - await sign({ event, user }); - await send(event); - } catch (e) { - warning("Nostr profile could not be updated"); - } - } - - if (user.email !== prev.email) { - try { - cookies.get = function (n) { - return this.find((c) => c.name === n).value; - }; - - user.verified = false; - - await post( - "/api/request", - { id: user.id, email: user.email }, - auth(cookies) - ); - - warning($t("user.settings.verifying"), false); - } catch (e) { - fail(e.message); - console.log(e); - } - } - - const response = await fetch(formElement.action, { - method: "POST", - body: data, - }); - - const result = deserialize(await response.text()); - - if (result.type === "success") { - await invalidateAll(); - if (data.get("password")) $password = data.get("password"); - } - - applyAction(result); - } catch (e) { - console.log(e); - fail("Something went wrong"); - } - - submitting = false; - } -</script> - -{#if user.haspin && $pin?.length !== 6} - <Pin /> -{/if} - -<form - method="POST" - class="mb-[154px] settings" - on:submit|preventDefault={handleSubmit} - bind:this={formElement} -> - <input type="hidden" name="pin" value={$pin} /> - <input type="hidden" name="tab" value={tab} /> - - <div class="mt-24 mb-20 px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8"> - <div class="header"> - <h1 class="text-center text-3xl md:text-4xl font-semibold mb-10"> - {$t("user.settings.header")} - </h1> - - <div - class="font-bold flex justify-around items-center border-b pb-3 text-secondary" - > - {#each tabs.filter((t) => t.name !== "shopify") as { name, key }} - <a href={`/${user.username}/settings/${name}`}> - <button - type="button" - class="hover:opacity-80" - class:text-black={tab === name || - (tab === "shopify" && name === "pos")} - >{$t(`user.settings.${key}`)}</button - > - </a> - {/each} - </div> - </div> - - <svelte:component this={comp} {user} {rates} {submit} /> - </div> - - <div - class="z-10 fixed bottom-0 bg-white shadow border-t w-full flex justify-center items-center py-3" - > - <button - bind:this={submit} - type="submit" - class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80" - class:bg-black={submitting} - > - {#if submitting} - <Spinner /> - {:else} - <div class="my-auto">{$t("user.settings.saveSettings")}</div> - {/if} - </button> - </div> -</form> - -<style> - .settings { - view-transition-name: settings; - } -</style> diff --git a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte deleted file mode 100644 index 8e3323c7..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte +++ /dev/null @@ -1,190 +0,0 @@ -<script> - import { tick } from "svelte"; - import { avatar, banner } from "$lib/store"; - import Icon from "$comp/Icon.svelte"; - import { t } from "$lib/translations"; - import { page } from "$app/stores"; - - export let user, rates, submit; - - let { id } = user; - - let avatarFile, avatarInput, bannerFile, bannerInput; - - let selectAvatar = () => avatarInput.click(); - let selectBanner = () => bannerInput.click(); - - let percent; - let progress = async (event) => { - percent = Math.round((event.loaded / event.total) * 100); - }; - - let tooLarge = {}; - - let handleFile = async ({ target }, type) => { - tooLarge[type] = false; - let file = target.files[0]; - if (!file) return; - - if (file.size > 10000000) return (tooLarge[type] = true); - - if (type === "profile") { - $avatar = { id, file, type, progress }; - } else if (type === "banner") { - $banner = { id, file, type, progress }; - } - - var reader = new FileReader(); - reader.onload = async (e) => { - if (type === "profile") { - $avatar.src = e.target.result; - } else if (type === "banner") { - $banner.src = e.target.result; - } - }; - - reader.readAsDataURL(file); - }; - - if (!user.display) user.display = user.username; - - $: url = `${$page.url.host}/${user.username}`; - $: full = `${$page.url.protocol}//${url}`; - $: addr = `${user.username}@${$page.url.host}`; -</script> - -<div> - <label for="username" class="font-bold mb-1 block" - >{$t("user.settings.username")}</label - > - <div class="flex mb-2"> - <input - type="text" - name="username" - bind:value={user.username} - class="w-auto min-w-0 pr-1 grow" - /> - </div> -</div> - -<div> - <label for="display" class="font-bold mb-1 block" - >{$t("user.settings.displayName")}</label - > - <input type="text" name="display" bind:value={user.display} /> -</div> - -<div class="space-y-1 relative"> - <label for="email" class="font-bold block">{$t("user.settings.email")}</label> - <p class="text-secondary"> - {$t("user.settings.emailDescription")} - </p> - - <div class="relative"> - <input type="text" name="email" bind:value={user.email} /> - {#if user.verified} - <Icon icon="check" style="absolute right-5 top-3 w-8" /> - {:else if user.email} - <Icon icon="orangeclock" style="absolute right-5 top-3 w-8" /> - {/if} - </div> -</div> - -<div> - <span class="font-bold">{$t("user.settings.profileImage")}</span> - - <div class="flex"> - {#if $avatar || user.profile} - <div - class="relative rounded-full overflow-hidden text-center w-20 h-20 my-auto hover:opacity-80 cursor-pointer" - on:click={selectAvatar} - on:keydown={selectAvatar} - > - <img - src={$avatar?.src || `/api/public/${user.profile}.webp`} - class="absolute w-full h-full object-cover object-center visible overflow-hidden" - alt={user.username} - /> - </div> - {:else} - <div - class="rounded-full border-4 border-white p-4 bg-gradient-to-r from-primary to-gradient w-24 my-auto hover:opacity-80 cursor-pointer" - on:click={selectAvatar} - on:keydown={selectAvatar} - > - <Icon icon="logo-symbol-white" style="mx-auto" /> - </div> - {/if} - <div class="ml-2 p-2"> - <!-- found missing translation --> - <button - type="button" - class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80" - on:click={selectAvatar} - on:keydown={selectAvatar}>{$t("user.settings.select")}</button - > - <input - type="file" - class="hidden" - bind:this={avatarInput} - on:change={(e) => handleFile(e, "profile")} - /> - </div> - </div> - - {#if tooLarge["avatar"]} - <div class="text-red-600">Max file size 10MB</div> - {/if} -</div> - -<div> - <div class="flex justify-between items-center"> - <span class="font-bold">{$t("user.settings.bannerImage")}</span> - </div> - - {#if $banner || user.banner} - <img - src={$banner ? $banner.src : `/api/public/${user.banner}.webp`} - class="w-full object-cover object-center visible overflow-hidden h-48 mb-4 hover:opacity-80" - on:click={selectBanner} - on:keydown={selectBanner} - alt="Banner" - /> - {:else} - <div - class="bg-gradient-to-r from-primary to-gradient w-full h-48 mb-4 cursor-pointer hover:opacity-80" - on:click={selectBanner} - on:keydown={selectBanner} - alt="Banner" - /> - {/if} - - <button - type="button" - class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80" - on:click={selectBanner} - on:keydown={selectBanner}>{$t("user.settings.select")}</button - > - <input - type="file" - class="hidden" - bind:this={bannerInput} - on:change={(e) => handleFile(e, "banner")} - /> - - {#if tooLarge["banner"]} - <div class="text-red-600">Max file size 10MB</div> - {/if} -</div> - -<div> - <label for="address" class="font-bold mb-1 block" - >{$t("user.settings.about")}</label - > - <textarea - type="text" - name="address" - bind:value={user.address} - placeholder={$t("user.settings.aboutPlaceholder")} - /> -</div> diff --git a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte deleted file mode 100644 index 6a1c92a1..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte +++ /dev/null @@ -1,238 +0,0 @@ -<script> - import { tick } from "svelte"; - import Icon from "$comp/Icon.svelte"; - import Numpad from "$comp/Numpad.svelte"; - import LocaleSelector from "$comp/LocaleSelector.svelte"; - import Toggle from "$comp/Toggle.svelte"; - import { locale, t } from "$lib/translations"; - import { success, fail } from "$lib/utils"; - import { page } from "$app/stores"; - - export let user, rates, submit; - - let { currency } = user; - let rate = rates[currency]; - - let fiats = Object.keys(rates).sort((a,b) => a.localeCompare(b)); - $: user.language = $locale; - let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click()); - - let editingReserve, editingThreshold, doneReserve, doneThreshold; - let doneEditing = () => { - editingReserve = false; - editingThreshold = false; - }; - - let editReserve = async () => { - editingReserve = true; - await tick(); - reserveEl.focus(); - }; - - let editThreshold = async () => { - editingThreshold = true; - await tick(); - thresholdEl.focus(); - }; - - if (!user.threshold) user.threshold = 1000000; - if (!user.reserve) user.reserve = 100000; - let reserveEl, thresholdEl; -</script> - -<div> - <label for="language" class="font-bold block mb-1" - >{$t("user.settings.locale")}</label - > - <LocaleSelector style="select-styles block py-3 w-full" /> -</div> - -<div> - <label for="currency" class="font-bold block mb-1" - >{$t("user.settings.localCurrency")}</label - > - <select - name="currency" - class="select-styles block py-3 w-full" - value={user.currency} - > - {#each fiats as fiat} - <option value={fiat}>{fiat}</option> - {/each} - </select> -</div> - -<div> - <label for="memoPrompt" class="font-bold mb-1 block" - >{$t("user.settings.memoPrompt")}</label - > - <textarea - name="memoPrompt" - placeholder={$t("user.settings.memoPromptPlaceholder")} - bind:value={user.memoPrompt} - /> -</div> - -<div> - <div class="flex justify-between items-center"> - <span class="font-bold">{$t("user.settings.notifications")}</span> - <Toggle id="notify" bind:value={user.notify} /> - </div> - <p class="text-secondary mt-1 w-9/12"> - {$t("user.settings.notificationsDesc")} - </p> -</div> - -<div> - <div class="flex justify-between items-center"> - <span class="font-bold">{$t("user.settings.tipPrompt")}</span> - <Toggle id="prompt" bind:value={user.prompt} /> - </div> - <p class="text-secondary mt-1 w-9/12"> - {$t("user.settings.tipPromptDescription")} - </p> -</div> - -<div> - <div class="flex justify-between items-center"> - <span class="font-bold">{$t("user.settings.publishNip5")}</span> - <Toggle id="nip5" bind:value={user.nip5} /> - </div> - <p class="text-secondary mt-1 w-9/12"> - {$t("user.settings.nip5a")} - <span class="font-bold">{user.username}@{$page.url.host}</span> - {$t("user.settings.nip5b")} - </p> -</div> - -<div> - <div class="flex justify-between items-center"> - <span class="font-bold">{$t("user.settings.autoWithdraw")}</span> - <Toggle id="autowithdraw" bind:value={user.autowithdraw} /> - </div> - <p class="text-secondary mt-1 w-9/12"> - {$t("user.settings.autoWithdrawDescription")} - </p> -</div> - -{#if user.autowithdraw} - <div class="mb-2"> - <label for="display" class="font-bold mb-1 block" - >{$t("user.settings.destination")}</label - > - <textarea - name="destination" - placeholder={$t("user.settings.destinationPlaceholder")} - on:keypress={keypress} - class="w-full p-4 border rounded-xl h-48" - bind:value={user.destination} - /> - </div> - - <div> - <label for="display" class="font-bold mb-1 block" - >{$t("user.settings.threshold")}</label - > - <button type="button" class="flex w-full" on:click={editThreshold}> - <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-primary"> - ⚡️ - </div> - <div - class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left" - > - {user.threshold} - </div> - <input type="hidden" name="threshold" bind:value={user.threshold} /> - </button> - <p class="text-secondary mt-1"> - {$t("user.settings.thresholdDesc")} - </p> - </div> - - <div> - <label for="display" class="font-bold mb-1 block" - >{$t("user.settings.reserve")}</label - > - <button type="button" class="flex w-full" on:click={editReserve}> - <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-primary"> - ⚡️ - </div> - <div - class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left" - > - {user.reserve} - </div> - <input type="hidden" name="reserve" bind:value={user.reserve} /> - </button> - <p class="text-secondary mt-1"> - {$t("user.settings.reserveDesc")} - </p> - </div> -{/if} - -{#if editingThreshold} - <div - class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50" - > - <div - class="relative top-40 mx-auto p-5 border shadow-lg rounded-md bg-white space-y-5 w-lg max-w-lg" - > - <h1 class="text-center text-2xl font-semibold"> - {$t("user.settings.threshold")} - </h1> - <Numpad - bind:amount={user.threshold} - bind:currency - bind:rate - bind:submit={doneReserve} - bind:element={thresholdEl} - /> - - <button - bind:this={doneReserve} - type="button" - on:click={doneEditing} - class="p-4 bg-black text-white rounded-2xl text-lg font-semibold w-full" - >Ok</button - > - </div> - </div> -{/if} - -{#if editingReserve} - <div - class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50" - > - <div - class="relative top-40 mx-auto p-5 border shadow-lg rounded-md bg-white space-y-5 w-lg max-w-lg text-center" - > - <h1 class="text-2xl font-semibold"> - {$t("user.settings.reserve")} - </h1> - <Numpad - bind:amount={user.reserve} - bind:currency - bind:rate - bind:submit={doneReserve} - bind:element={reserveEl} - /> - <button - bind:this={doneReserve} - type="button" - on:click={doneEditing} - class="p-4 bg-black text-white rounded-2xl text-lg font-semibold w-full" - >Ok</button - > - </div> - </div> -{/if} - -<a href="./shopify"> - <button - type="button" - class="flex rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full gap-2 my-5 justify-center" - > - <img src="/icons/shopify.svg" class="w-8" /> - <div class="my-auto">{$t("user.settings.shopify")}</div> - </button> -</a> diff --git a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte deleted file mode 100644 index 6b44f830..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte +++ /dev/null @@ -1,321 +0,0 @@ -<script> - import { getMnemonic, getNsec } from "$lib/nostr"; - import { tick } from "svelte"; - import { t } from "$lib/translations"; - import Pin from "$comp/Pin.svelte"; - import Icon from "$comp/Icon.svelte"; - import Qr from "$comp/Qr.svelte"; - import { copy, post, success, fail } from "$lib/utils"; - import { pin as current } from "$lib/store"; - import { invalidate } from "$app/navigation"; - import { page } from "$app/stores"; - - export let user, rates, submit; - - let confirming2fa, - disabling2fa, - importing, - locked, - mnemonic, - newNsec, - nsec, - old, - otp, - password, - pin, - revealPassword, - revealSeed, - revealNsec, - token = "", - setting2fa, - settingPin, - verify; - - let toggleImporting = () => { - revealNsec = false; - importing = !importing; - }; - - let toggleNsec = async () => { - try { - nsec = await getNsec(user); - revealNsec = !revealNsec; - importing = false; - } catch (e) { - console.log(e); - } - }; - - let togglePassword = () => (revealPassword = !revealPassword); - let toggleSeed = async () => { - try { - mnemonic = await getMnemonic(user); - revealSeed = !revealSeed; - } catch (e) { - console.log(e); - } - }; - - $: verifying = pin?.length > 5; - $: verify && checkPin(pin); - let checkPin = async () => { - old = $current; - - try { - if (pin.length > 5 && pin === verify) { - user.haspin = true; - $current = pin; - pin = ""; - verify = ""; - submit.click(); - settingPin = false; - verifying = false; - } else { - fail("Pin mismatch, try again"); - pin = ""; - verify = ""; - verifying = false; - settingPin = true; - } - } catch (e) { - console.log(e); - fail("Problem setting PIN"); - } - }; - - let reset = () => { - token = ""; - return true; - }; - - let disablingPin = false; - - let togglePin = async () => { - if (user.haspin) { - try { - disablingPin = true; - await tick(); - submit.click(); - } catch (e) { - console.log(e); - fail("Failed to disable pin"); - } - } else { - settingPin = true; - disablingPin = false; - } - }; - - let startEnabling2fa = async () => { - reset(); - try { - if (!otp) otp = await post("/otpsecret", { pin: $current }); - } catch (e) { - console.log(e); - } - - setting2fa = true; - }; - - let startDisabling2fa = () => (disabling2fa = true); - let startConfirming2fa = () => (confirming2fa = true); - let cancel = () => { - pin = null; - token = null; - verifying = false; - settingPin = false; - setting2fa = false; - confirming2fa = false; - disabling2fa = false; - }; - - $: enable2fa(token); - let enable2fa = async (twoFa) => { - try { - if (setting2fa && token.length === 6) { - await post("/enable2fa", { token }); - success("2FA enabled"); - user.twofa = 1; - cancel(); - } - } catch (e) { - fail("Failed to enable 2FA, try again"); - } - }; - - $: disable2fa(token); - let disable2fa = async () => { - try { - if (disabling2fa && token.length === 6) { - await post("/disable2fa", { token }); - success("2FA disabled"); - delete user.twofa; - disabling2fa = false; - } - } catch (e) { - console.log(e); - fail("Failed to disable 2FA, try again"); - } - }; -</script> - -<input type="hidden" name="newpin" value={disablingPin ? "delete" : pin} /> -<input type="hidden" name="confirm" bind:value={password} /> - -<div class="relative mb-5"> - <label for="password" class="block font-bold block mb-1" - >{$t("user.settings.newPassword")}</label - > - {#if revealPassword} - <input name="password" type="text" bind:value={password} /> - {:else} - <input name="password" type="password" bind:value={password} /> - {/if} - <button - type="button" - on:click={togglePassword} - on:keydown={togglePassword} - class="absolute right-5 top-11" - > - <Icon icon={revealPassword ? "eye" : "eye-off"} /> - </button> -</div> - -<div> - <span class="font-bold mb-1" - >{verifying - ? $t("user.settings.verifyPIN") - : $t("user.settings.securityPIN")}</span - > - <p class="text-secondary mb-1"> - {$t("user.settings.securityPINDescription")} - </p> - {#if verifying} - <Pin bind:value={verify} {cancel} notify={false} /> - {:else if settingPin} - <Pin - bind:value={pin} - title={$t("user.settings.setPIN")} - {cancel} - notify={false} - /> - {:else} - <button type="button" class="primary" on:click={togglePin} - ><Icon icon="lock" style="mr-1" /> - {user.haspin - ? $t("user.settings.disablePIN") - : $t("user.settings.enablePIN")}</button - > - {/if} -</div> - -<div> - <span class="font-bold mb-1">{$t("user.settings.twofa")}</span> - <p class="text-secondary mb-4"> - {$t("user.settings.twofaDescription")} - </p> - - {#if setting2fa} - <a href={otp.uri}> - <Qr src={otp.qr} /> - </a> - - <div class="text-center my-4"> - {$t("user.settings.accountId")}<br /> - <b>{otp.secret}</b> - </div> - - <button type="button" class="primary" on:click={startConfirming2fa}> - <Icon icon="numpad" style="w-8 mr-1" /> - <div class="my-auto">Confirm</div> - </button> - {:else if user.twofa} - <button type="button" class="primary" on:click={startDisabling2fa}> - <Icon icon="mobile" style="w-8 mr-1" /> - <div class="my-auto">{$t("user.settings.twofaDisable")}</div> - </button> - {:else} - <button type="button" class="primary" on:click={startEnabling2fa}> - <Icon icon="mobile" style="w-8 mr-1" /> - <div class="my-auto">{$t("user.settings.twofaSetup")}</div> - </button> - {/if} - - {#if confirming2fa || disabling2fa} - <Pin - bind:value={token} - title="Enter 2FA Code" - {cancel} - persist={false} - notify={false} - /> - {/if} -</div> - -<!-- <div> --> -<!-- <label for="seedphrase" class="font-bold" --> -<!-- >{$t("user.settings.seedPhrase")}</label --> -<!-- > --> -<!-- --> -<!-- <p class="text-secondary mb-1"> --> -<!-- {$t("user.settings.seedDescription")} --> -<!-- </p> --> -<!-- --> -<!-- <button type="button" class="primary" on:click={toggleSeed}> --> -<!-- <Icon icon="warning" style="mr-1 w-6 my-auto" /> --> -<!-- {revealSeed ? $t("user.settings.hideSeed") : $t("user.settings.revealSeed")} --> -<!-- </button> --> -<!-- --> -<!-- {#if revealSeed} --> -<!-- <div class="text-lg"> --> -<!-- {mnemonic} --> -<!-- </div> --> -<!-- --> -<!-- <button type="button" class="primary" on:click={() => copy(mnemonic)}> --> -<!-- <Icon icon="copy" style="mr-1 w-6 my-auto" /> --> -<!-- Copy --> -<!-- </button> --> -<!-- {/if} --> -<!-- </div> --> - -<div> - <label for="seedphrase" class="font-bold" - >{$t("user.settings.nostrKeys")}</label - > - - <p class="text-secondary mb-1"> - {$t("user.settings.nostrDescription")} - </p> - - <div class="flex"> - <button type="button" class="primary" on:click={toggleNsec}> - {#if revealNsec} - <Icon icon="eye-off" style="mr-1 w-6 my-auto" /> - {$t("user.settings.hideNsec")} - {:else} - <Icon icon="warning" style="mr-1 w-6 my-auto" /> - {$t("user.settings.revealNsec")} - {/if} - </button> - - <button type="button" class="primary" on:click={toggleImporting}> - <Icon icon="save" style="mr-1 w-6 my-auto" /> - {$t("user.settings.import")} - </button> - </div> - - {#if importing} - <input name="newNsec" bind:value={newNsec} placeholder="nsec..." /> - {/if} - - {#if revealNsec} - <div class="text-lg break-all"> - {nsec} - </div> - - <button type="button" class="primary" on:click={() => copy(nsec)}> - <Icon icon="copy" style="mr-1 w-6 my-auto" /> - Copy - </button> - {/if} -</div> diff --git a/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte b/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte deleted file mode 100644 index 0266fa9c..00000000 --- a/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte +++ /dev/null @@ -1,92 +0,0 @@ -<script> - import { t } from "$lib/translations"; - import Icon from "$comp/Icon.svelte"; - import { page } from "$app/stores"; - - export let user; - $: paymentsUrl = - user.shopifyStore && - "https://admin.shopify.com/store/" + - user.shopifyStore + - "/settings/payments"; - $: checkoutUrl = - user.shopifyStore && - "https://admin.shopify.com/store/" + - user.shopifyStore + - "/settings/checkout"; - $: appsUrl = - user.shopifyStore && - "https://admin.shopify.com/store/" + - user.shopifyStore + - "/settings/apps/development"; - - let script = `<script src="${$page.url.protocol}//${$page.url.host}/${user.username}/shopify.js"><\/script>`; -</script> - -<div> - <label for="shopifyStore" class="font-bold mb-1 block">Store ID</label> - <div class="flex mb-2"> - <div - class="text-gray-600 p-4 my-auto bg-gray-100 border rounded-l-2xl border-r-0 pr-1" - > - admin.shopify.com/store/ - </div> - <input - type="text" - name="shopifyStore" - bind:value={user.shopifyStore} - class="border-l-0 rounded-l-none w-auto min-w-0 pl-1 grow" - /> - </div> - <p class="text-secondary my-2"> - Add Bitcoin under <a - class:active={!!paymentsUrl} - href={paymentsUrl || undefined}>Payments > Manual payment methods</a - > with a description like "Pay with Bitcoin". - </p> -</div> - -<div> - <label for="display" class="font-bold mb-1 block">Additional scripts</label> - <p class="text-secondary my-2"> - Copy this into <a class:active={!!paymentsUrl} href={checkoutUrl} - >Checkout -> Additional scripts</a - > - </p> - <textarea rows={5} type="text" name="shopifyScripts" readonly - >{script}</textarea - > -</div> - -<div> - <label for="shopifyToken" class="font-bold mb-1 block" - >Admin API token <span class="font-normal">(Optional)</span></label - > - <p class="text-secondary my-2"> - Coinos can automatically mark orders as Paid if you provide an API token. - <input - class="my-4" - type="text" - name="shopifyToken" - bind:value={user.shopifyToken} - /> - </p> - <p class="text-secondary my-2"> - Create a new app in <a class:active={!!appsUrl} href={appsUrl || undefined} - >Apps > App Development</a - > - </p> - - <p class="text-secondary my-2"> - Edit the Admin API scopes to enable <code class="text-black" - >write_orders</code - > permission then install the app into your store and copy the Admin API access - token here. - </p> -</div> - -<style> - .active { - @apply text-blue-500; - } -</style> diff --git a/src/routes/(app)/[username]/verify/[code]/+page.server.ts b/src/routes/(app)/[username]/verify/[code]/+page.server.ts deleted file mode 100644 index fec1a85c..00000000 --- a/src/routes/(app)/[username]/verify/[code]/+page.server.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { get } from "$lib/utils"; -import { redirect } from "@sveltejs/kit"; - -export let load = async ({ params: { code } }) => { - let user; - try { - user = await get(`/verify/${code}`); - } catch (e) { - console.log(e); - return { error: "Verification failed" }; - } - - redirect(307, `/${user.username}/settings/account?verified=true`); -}; diff --git a/src/routes/(app)/[username]/verify/[code]/+page.svelte b/src/routes/(app)/[username]/verify/[code]/+page.svelte deleted file mode 100644 index 1e099dae..00000000 --- a/src/routes/(app)/[username]/verify/[code]/+page.svelte +++ /dev/null @@ -1,35 +0,0 @@ -<script> - import { scale } from "svelte/transition"; - import Icon from "$comp/Icon.svelte"; - import { t } from "$lib/translations"; - - export let data; - let { user, error } = data; -</script> - -<div class="container px-4 text-center mx-auto"> - {#if error} - <h1 class="text-3xl md:text-4xl font-bold mb-6"> - Email verification failed - </h1> - {:else if user.verified} - <div - class="flex w-full py-5 max-w-[200px] mx-auto" - in:scale={{ start: 0.5 }} - > - <Icon icon="check" style="mx-auto" /> - </div> - - <h1 class="text-3xl md:text-4xl font-bold mb-6"> - Email verified successfully! - </h1> - {/if} -</div> - -<a href={`/${user.username}`}> - <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" /> -</a> - -<div class="fixed bottom-10 w-full text-center"> - {$t("payments.tapAnywhere")} -</div> |
